#!/usr/bin/env python
#encoding = utf-8

#some utility function for session and other class

class ThreadedDict:
	def __getattr__(self, key):
		return getattr(self._getd(), key)

	def __setattr__(self, key, value):
		return setattr(self._getd(), key, value)

	def __delattr__(self, key):
		return delattr(self._getd(), key)

	def __hash__(self): 
		return id(self)

	def _getd(self):
		t = threading.currentThread()
		if not hasattr(t, '_d'):
			# using __dict__ of thread as thread local storage
			t._d = {}

		# there could be multiple instances of ThreadedDict.
		# use self as key
		if self not in t._d:
			t._d[self] = storage()
		return t._d[self]

threadeddict = ThreadedDict

def convert_encode(obj, from_enc, to_enc, errors='strict'):
	if isinstance(obj, (list, tuple)):
		return type(obj)([convert_encode(o, from_enc, to_enc, errors) for o in obj])
	elif isinstance(obj, dict):
		return dict([[convert_encode(k, from_enc, to_enc, errors),
					convert_encode(v, from_enc, to_enc, errors)]
					for k, v in obj.iteritems()])
	elif isinstance(obj, unicode):
		return obj.encode(to_enc, errors)
	elif isinstance(obj, str):
		return unicode(obj, from_enc, errors).encode(to_enc, errors)
	return obj


def to_unicode(data, encoding="GB18030"):
	"""convert data from some encoding to unicode
	data could be string, list, tuple or dict
	that contains string as key or value
	"""
	if data is None:
		return unicode('')
	if isinstance(data, unicode):
		return data

	if isinstance(data, (list, tuple)):
		u_data = []
		for item in data:
			u_data.append(to_unicode(item, encoding))

	elif isinstance(data, dict):
		u_data = {}
		for key in data:
			u_data[to_unicode(key, encoding)] = to_unicode(data[key], encoding)

#   elif isinstance(data, str) or\
#           (hasattr(data, '__str__') and callable(data.__str__)):
	elif isinstance(data, str):
		u_data = unicode(data, encoding, 'ignore')

	else:
		u_data = data

	return unicode(u_data)

class UObj:
	#do not gen UObj when input obj type is in base_types
	base_types = (
		bool, float, int, long, complex, unicode,
	)
	#do not gen UObj when attr name in raw_attrs
	raw_attrs = (
		'__name__', '__coerce__',
	)
	#gen UObj with fake rop when input obj doesn't have the attr and
	#attr is in rops
	rops = (
		'__radd__', '__rdiv__', '__rmod__', '__rmul__', '__rsub__',
		'__rand__', '__rlshift__', '__ror__', '__rrshift__', '__rxor__',
		'__rdivmod__', '__rpow__',
	)

	def __init__(self, obj, encoding, fake_rop):
		self._obj = obj
		self._encoding = encoding
		self._fake_rop = fake_rop

	@classmethod
	def _gen_rop_name(self, name):
		"""
		gen fake rop name, just removing the first 'r' in original name
		"""
		return name.replace('r', '', 1)

	@classmethod
	def _cvt_arg(self, arg):
		"""
		single argument conversion, return internal obj if arg is an UObj instance
		"""
		if isinstance(arg, UObj):
			return arg._obj
		return arg

	@classmethod
	def _cvt_args(self, *args):
		"""
		sequence argument conversion
		"""
		return [UObj._cvt_arg(a) for a in args]

	@classmethod
	def _cvt_kwargs(self, **kwargs):
		"""
			keyword argument conversion
		"""
		new_kwargs = {}
		for key, value in kwargs.iteritems():
			new_kwargs[UObj._cvt_arg(key)] = UObj._cvt_arg(value)
		return new_kwargs

	def __eq__(self, other):
		return self.__cmp__(other) == 0

	def __ne__(self, other):
		return self.__cmp__(other) != 0

	def __lt__(self, other):
		return self.__cmp__(other) < 0

	def __gt__(self, other):
		return self.__cmp__(other) > 0

	def __le__(self, other):
		return self.__cmp__(other) <= 0

	def __ge__(self, other):
		return self.__cmp__(other) >= 0

	def __cmp__(self, other):
		other = UObj._cvt_arg(other)
		if self._obj == other:
			return 0
		elif self._obj > other:
			return 1
		else:
			return -1

	def __unicode__(self):
		return to_unicode(self._obj, self._encoding)

	def __getattr__(self, name):
		fake_rop = False
		if (not hasattr(self._obj, name)) and (name in UObj.rops):
			new_name = UObj._gen_rop_name(name)
			if hasattr(self._obj, new_name):
				name = new_name
				fake_rop = True
		attr = getattr(self._obj, name)
		if name in UObj.raw_attrs:
			return attr
		return gen_uobj(attr, self._encoding, fake_rop)

	def __call__(self, *args, **kwargs):
		if self._fake_rop:
			return gen_uobj(getattr(args[0], self.__name__)(self._obj.__self__), self._encoding)
		return gen_uobj(self._obj(*(UObj._cvt_args(*args)), **(UObj._cvt_kwargs(**kwargs))),
						self._encoding)

	def origin(self):
		return self._obj


def gen_uobj(obj, encoding="GB18030", fake_rop=False):
	if isinstance(obj, str):
		return obj.decode(encoding, 'ignore')

	if not obj or isinstance(obj, UObj.base_types):
		return obj

	return UObj(obj, encoding, fake_rop)


def import_var(mod_name, *var_names):
	if not var_names:
		try:
			mod_name, var_names = mod_name.rsplit('.', 1)
			var_names = [var_names]
		except ValueError:
			pass

	mod = __import__(mod_name, globals(), locals(), var_names)
	if not var_names:
		return mod
	elif len(var_names) == 1:
		return getattr(mod, var_names[0])
	return [getattr(mod, var_name) for var_name in var_names]


def import_builtin(*builtin_names):
	return import_var('__builtin__', *builtin_names)

class RequestDict(dict):
	def __init__(self, *largs, **kwargs):
		dict.__init__(self, *largs, **kwargs)

	def get(self, key, default=None):
		result = self[key]
		if result is None:
			result = default

		return result

	def getall(self, key, default=None):
		if default is None:
			default = []
		return dict.get(self, key, default)

	def __getitem__(self, key):
		try:
			return dict.__getitem__(self, key)[0]
		except (IndexError, KeyError), e:
			return None

	def __setitem__(self, key, value):
		qs = self.setdefault(key, [])
		qs.append(value)

	def __call__(self, key):
		return self.__getitem__(key)

	def update(self, newdict):
		for k, v in newdict.items():
			nv = self.setdefault(k, [])
			nv.extend(v)

	def to_dict(self):
		dict = {}
		for k, v in self.items():
			if not v:
				dict[k] = None
			elif len(v) == 1:
				dict[k] = v[0]
			else:
				dict[k] = v
		return dict

if __name__ == '__main__':
	pass
